//
// Created by hamlet on 23-3-12.
//

#include "Util"

namespace FansionML {
    // 写入和读取Eigen矩阵的方法来自网传代码：
    // https://gist.github.com/zishun/da277d30f4604108029d06db0e804773#file-eigen_matrix_io-cpp

    using Eigen::Matrix;

    template<class Matrix>
    inline void writeMatrix(std::ofstream &out, const Matrix &data) {
        typename Matrix::Index rows = data.rows(), cols = data.cols();
        out.write(reinterpret_cast<char *>(&rows), sizeof(typename Matrix::Index));
        out.write(reinterpret_cast<char *>(&cols), sizeof(typename Matrix::Index));
        out.write(reinterpret_cast<const char *>(data.data()),
                  rows * cols * static_cast<typename Matrix::Index>(sizeof(typename Matrix::Scalar)));
    }

    void writeLabel(std::ofstream &out, const LabelT &data) {
        writeMatrix(out, data);
    }

    template<class Matrix>
    inline void readMatrix(std::ifstream &in, Matrix &data) {
        typename Matrix::Index rows = 0, cols = 0;
        in.read(reinterpret_cast<char *>(&rows), sizeof(typename Matrix::Index));
        in.read(reinterpret_cast<char *>(&cols), sizeof(typename Matrix::Index));
        data.resize(rows, cols);
        in.read(reinterpret_cast<char *>(data.data()),
                rows * cols * static_cast<typename Matrix::Index>(sizeof(typename Matrix::Scalar)));
    }

    void readLabel(std::ifstream &in, LabelT &data) {
        readMatrix(in, data);
    }

    using Eigen::SparseMatrix;

    template<class SparseMatrix>
    inline void writeSparse(std::ofstream &out, const SparseMatrix &data) {
        assert(data.isCompressed());
        typename SparseMatrix::Index rows, cols, nnZs, outS, innS;
        rows = data.rows();
        cols = data.cols();
        nnZs = data.nonZeros();
        outS = data.outerSize();
        innS = data.innerSize();

        auto sizeIndex = static_cast<typename SparseMatrix::Index>(sizeof(typename SparseMatrix::Index));
        out.write(reinterpret_cast<char *>(&rows), sizeIndex);
        out.write(reinterpret_cast<char *>(&cols), sizeIndex);
        out.write(reinterpret_cast<char *>(&nnZs), sizeIndex);
        out.write(reinterpret_cast<char *>(&outS), sizeIndex);
        out.write(reinterpret_cast<char *>(&innS), sizeIndex);

        auto sizeIndexS = static_cast<typename SparseMatrix::Index>(sizeof(typename SparseMatrix::StorageIndex));
        auto sizeScalar = static_cast<typename SparseMatrix::Index>(sizeof(typename SparseMatrix::Scalar));
        out.write(reinterpret_cast<const char *>(data.valuePtr()), sizeScalar * nnZs);
        out.write(reinterpret_cast<const char *>(data.outerIndexPtr()), sizeIndexS * outS);
        out.write(reinterpret_cast<const char *>(data.innerIndexPtr()), sizeIndexS * nnZs);
    }

    void writeData(std::ofstream &out, const DataT &data) {
        writeSparse(out, data);
    }

    template<class SparseMatrix>
    inline void readSparse(std::ifstream &in, SparseMatrix &data) {
        typename SparseMatrix::Index rows, cols, nnz, inSz, outSz;
        auto sizeScalar = static_cast<typename SparseMatrix::Index>(sizeof(typename SparseMatrix::Scalar));
        auto sizeIndex = static_cast<typename SparseMatrix::Index>(sizeof(typename SparseMatrix::Index));
        auto sizeIndexS = static_cast<typename SparseMatrix::Index>(sizeof(typename SparseMatrix::StorageIndex));
        in.read(reinterpret_cast<char *>(&rows ), sizeIndex);
        in.read(reinterpret_cast<char *>(&cols ), sizeIndex);
        in.read(reinterpret_cast<char *>(&nnz  ), sizeIndex);
        in.read(reinterpret_cast<char *>(&outSz), sizeIndex);
        in.read(reinterpret_cast<char *>(&inSz ), sizeIndex);

        data.resize(rows, cols);
        data.makeCompressed();
        data.resizeNonZeros(nnz);

        in.read(reinterpret_cast<char *>(data.valuePtr()), sizeScalar * nnz);
        in.read(reinterpret_cast<char *>(data.outerIndexPtr()), sizeIndexS * outSz);
        in.read(reinterpret_cast<char *>(data.innerIndexPtr()), sizeIndexS * nnz);

        data.finalize();
    }

    void readData(std::ifstream &in, DataT &data) {
        readSparse(in, data);
    }

    void writeName(std::ofstream &out, const TextT &data) {
        IndexT size = data.size();
        out.write(reinterpret_cast<char *>(&size), sizeof(size));
        for (const auto &text: data) {
            size_t sz = text.size();
            out.write(reinterpret_cast<char *>(&sz), sizeof(sz));
            out.write(&text[0], static_cast<std::streamsize>(sz));
        }
    }

    void readName(std::ifstream &in, TextT &data) {
        data.clear();
        IndexT size;
        in.read(reinterpret_cast<char *>(&size), sizeof(size));
        data.reserve(size);
        for (IndexT i = 0; i < size; ++i) {
            size_t sz;
            in.read(reinterpret_cast<char *>(&sz), sizeof(sz));
            TextT::value_type text;
            text.resize(sz);
            in.read(&text[0], static_cast<std::streamsize>(sz));
            data.emplace_back(std::move(text));
        }
    }

}
